home *** CD-ROM | disk | FTP | other *** search
- /* RE_SID: @(%)/tmp_mnt/vol/dosnfs/shades_SCCS/unix/rfcmsg/server/src/SCCS/s.msgserv.c 1.6 93/02/08 10:25:17 SMI */
- /* @(#)msgserv.c 1.6 2/8/93 */
- /*
- * msgserv.c
- *
- * Copyright (c) 1991-1993 Sun Microsystems, Inc.
- *
- * This is a server implementation of the Message Send protocol
- * defined in RFCxxxx. This implementation may be freely
- * copied, modified, and redistributed, provided that this
- * comment and the Sun Microsystems copyright are retained.
- * Anyone installing, modifying, or documenting this
- * software is advised to read the section in the RFC which
- * deals with security issues.
- */
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/stat.h>
- #include <utmp.h>
- #ifdef SVR4
- #include <sys/select.h>
- #include <sys/resource.h>
- #endif
- #include <sys/socket.h>
- #ifndef SVR4
- #include <sys/ttycom.h>
- #endif
- #include <sys/wait.h>
- #include <sys/sockio.h>
- #include <netinet/in.h>
- #include <net/if.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <string.h>
- #include <signal.h>
- #include <ctype.h>
- #include <time.h>
- #include <memory.h>
-
- char *prog;
- int debug = 0;
- int verbose = 0;
- int use_console = 0; /* XXX currently unused */
- char *empty_arg = "";
-
- char * recipient;
- char * recip_term;
- char * sender;
- char * sender_term;
- char * msg_text;
- char * cookie;
- char * signature;
-
- char console[] = "/dev/console";
- /* utmp globals */
- struct utmp utmp;
- FILE *utmp_file; /* stream for utmp, also non-0 indicates valid entry */
-
-
- /*
- * types for all procedures
- */
- void usage();
- void handle_udp();
- void handle_tcp();
- int main();
- #ifdef SVR4
- void reaper();
- #else
- int reaper();
- #endif
- void udp_ack();
- void ack();
- void nak();
- char *deliver();
- int rip_apart_message();
- int check_cache();
- void filter();
- char *do_cmd();
- char * first_utmp_entry();
- void next_utmp_entry();
- char *send_to_term();
-
-
- void
- usage()
- {
- fprintf(stderr, "usage: %s [-d][-pN]\n", prog);
- /* XXX fprintf(stderr, "usage: %s [-d][-pN][-c]\n", prog); */
- fprintf(stderr, " -d - turn on debugging\n");
- fprintf(stderr, " -pN - use port N (default: 18)\n");
- /* XXX fprintf(stderr, " -c - ignore terminal and use console\n"); */
-
- }
-
- int
- main(argc, argv)
- int argc;
- char *argv[];
- {
-
- short port = 0;
- int tcpsock1;
- int tcpsock2;
- int udpsock;
- int i;
- fd_set ready;
- struct sockaddr_in sin;
- struct servent *sp;
-
- #ifdef SVR4
- int nfds;
- struct rlimit rlp;
-
- if (getrlimit(RLIMIT_NOFILE, &rlp) && debug)
- perror("getrlimit");
- nfds = rlp.rlim_cur;
- #else
- int nfds = getdtablesize();
- #endif
-
-
- prog = *argv++;
- argc--;
-
- /* process options:
- * -d (debug)
- * -pN (use port N instead of 18)
- */
-
- while(argc && *argv[0] == '-') {
- (*argv)++;
- switch (toupper(*argv[0])){
- case 'D':
- debug++;
- verbose++;
- break;
- case 'C':
- use_console++;
- break;
- case 'P':
- (*argv)++;
- port = atoi(*argv);
- break;
- default:
- usage();
- exit(1);
- /*NOTREACHED*/
- }
- argv++;
- argc--;
- }
- if(argc != 0) {
- usage();
- exit(1);
- /*NOTREACHED*/
- }
-
- if(!debug) {
- if(fork())
- exit(0);
- for(i = nfds-1; i >= 0; --i)
- close(i);
- (void)open("/dev/null", O_RDWR);
- dup2(0, 1);
- dup2(0, 2);
- #ifdef SVR4
- setsid();
- #else
- /* NB - setsid() also works in SunOS but maybe not other BSD-derived code */
- i = open("/dev/tty", O_RDWR);
- if(i >= 0){
- ioctl(i, TIOCNOTTY, 0);
- close(i);
- }
- #endif
- /* XXX todo - add code to use SYSLOG if we're not in debug mode */
- }
-
- signal(SIGCHLD, reaper);
-
- sin.sin_family = AF_INET;
-
- /*
- * compute the port to use: consult /etc/services, but if not
- * found use 18 (from the RFC). the -pN option overrides
- */
-
- if(port == 0) {
- sp = getservbyname("message", "udp");
- if(sp)
- sin.sin_port = sp->s_port;
- else
- sin.sin_port = htons(18); /* from the RFC */
- }
- else
- sin.sin_port = htons(port);
-
- sin.sin_addr.s_addr = INADDR_ANY;
-
- if(debug) printf("%s: using port %d\n", prog, htons(sin.sin_port));
-
-
-
- tcpsock1 = socket(AF_INET, SOCK_STREAM, 0);
- if(bind(tcpsock1, (struct sockaddr *)&sin, sizeof sin) < 0) {
- if(debug) perror("bind (TCP)");
- exit(99); /* XXX */
- }
- listen(tcpsock1, 5);
-
- udpsock = socket(AF_INET, SOCK_DGRAM, 0);
- if(bind(udpsock, (struct sockaddr *)&sin, sizeof sin) < 0) {
- if(debug) perror("bind (UDP)");
- exit(99); /* XXX */
- }
-
- if(debug) printf("entering main loop...\n");
- while(1) {
- FD_ZERO(&ready);
- FD_SET(udpsock, &ready);
- FD_SET(tcpsock1, &ready);
- i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
- if(debug) printf("----------------------------------------------------------\nselect returned %d\n", i);
- if(i <= 0) {
- if(debug)perror("select");
- continue;
- }
- if(FD_ISSET(udpsock, &ready))
- handle_udp(udpsock);
- else if(FD_ISSET(tcpsock1, &ready)) {
- tcpsock2 = accept(tcpsock1, (struct sockaddr *)0,
- (int *)0);
-
- if(debug)
- printf("forking....\n");
-
- if(fork() == 0) {
- close(tcpsock1);
- close(udpsock);
- handle_tcp(tcpsock2);
- exit(0);
- }
- }
- }
- }
-
- #define CACHE_ENTRIES 32
-
- struct mc_entry {
- struct sockaddr_in mc_addr;
- char mc_cookie[48];
- };
-
- int mc_used = 0;
- int mc_next = 0;
- struct mc_entry mcache[CACHE_ENTRIES];
-
- int
- check_cache(cookie, addrp)
- char *cookie;
- struct sockaddr_in *addrp;
- {
- int i;
- if(mc_used) {
- for (i = 0; i < mc_used; i++) {
- if(!strcmp(cookie, mcache[i].mc_cookie) &&
- !memcmp((char *)addrp, (char *)&mcache[i].mc_addr,
- sizeof(*addrp)))
- return(1);
- }
- }
- memcpy((char *)&mcache[mc_next].mc_addr, (char *)addrp, sizeof(*addrp));
- strcpy(mcache[mc_next].mc_cookie, cookie);
-
- mc_next++;
- if(mc_next > mc_used) mc_used = mc_next;
- mc_next = mc_next % CACHE_ENTRIES;
- return(0);
- }
-
- void
- handle_udp(s)
- int s;
- {
- char buff[1024];
- int buflen;
- struct sockaddr_in from;
- int fromlen;
- char *txt;
-
- fromlen = sizeof (from);
-
- if(debug) printf("%s: udp msg received\n", prog);
-
- buflen = recvfrom(s, buff, 1024, 0,
- (struct sockaddr *)&from, &fromlen);
-
- if(buflen < 0) {
- perror("recvfrom");
- return;
- }
- if(rip_apart_message(buff, buflen)) {
- fprintf(stderr, "%s: malformed message\n", prog);
- return;
- }
- if(check_cache(cookie, &from)) {
- if(debug) printf("duplicate message\n");
- return;
- }
-
- if(debug)
- printf("forking....\n");
-
- if(!debug) {
- if(fork() != 0)
- return;
- }
-
- if((txt = deliver()) == NULL && *recipient) {
- udp_ack(s, &from, "OK");
- }
- if(!debug)
- exit(0);
- }
-
- void
- handle_tcp(s)
- int s;
- {
- char buff[1024];
- int buflen;
- struct sockaddr_in peer;
- int peerlen;
- char *txt;
-
- if(debug) printf("%s: tcp msg received\n", prog);
-
- peerlen = sizeof peer;
- if(getpeername(s, &peer, &peerlen) < 0) {
- perror("getpeername");
- exit(99);
- }
-
- buflen = read(s, buff, 1024);
- if(buflen < 0) {
- perror("read");
- nak(s, "Read error");
- return;
- }
- if(rip_apart_message(buff, buflen)) {
- fprintf(stderr, "%s: malformed message\n", prog);
- nak(s, "Message format error");
- return;
- }
- if((txt = deliver()) != NULL) {
- #ifdef notyet
- nak(s, txt);
- return;
- #endif
- }
- ack(s, "OK");
- }
-
-
- /* Note the type difference here */
-
- #ifdef SVR4
- void
- reaper()
- {
- int i, j;
- i = wait(&j);
- return;
- }
- #else
- int
- reaper()
- {
- union wait status;
- while(wait3(&status, WNOHANG, 0) > 0) continue;
- return(0);
- }
- #endif
-
- void udp_ack(s, to, msg)
- int s;
- struct sockaddr_in *to;
- char *msg;
- {
- char buff[128];
- if(debug)
- printf("sending ack\n");
- sprintf(buff, "+%s", msg);
- (void)sendto(s, buff, strlen(buff) + 1, 0,
- (struct sockaddr *)to, sizeof (*to));
- }
-
- void ack(s, msg)
- int s;
- char *msg;
- {
- char buff[128];
- if(debug)
- printf("sending ack\n");
- sprintf(buff, "+%s", msg);
- (void)write(s, buff, strlen(buff) + 1);
- }
-
- void nak(s, msg)
- int s;
- char *msg;
- {
- char buff[128];
- if(debug)
- printf("sending nak\n");
- sprintf(buff, "-%s", msg);
- (void)write(s, buff, strlen(buff) + 1);
- }
-
- int
- rip_apart_message(buff, buflen)
- char *buff;
- int buflen;
- {
- char *cp1;
- char *cp2;
- char *lim;
-
-
- recipient = NULL;
- recip_term = NULL;
- sender = NULL;
- sender_term = NULL;
- msg_text = NULL;
- cookie = NULL;
- signature = NULL;
-
- if(buff[0] != 'B')
- return(1);
-
- lim = &buff[buflen];
-
- cp1 = buff;
- cp1++; /* point at recipient */
-
- /*
- * Gather up recipient
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- recipient = cp1;
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("recipient = '%s'\n", recipient);
-
-
-
- /*
- * Gather up recip_term
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- recip_term = cp1;
-
- /* toss preceding "/dev/" if any */
- if (strncmp(recip_term, "/dev/", strlen("/dev/")) == 0)
- recip_term += strlen("/dev/");
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("recip_term = '%s'\n", recip_term);
-
-
- /*
- * Gather up msg_text
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- msg_text = cp1;
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("msg_text = '%s'\n", msg_text);
-
-
- /*
- * Gather up sender
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- sender = cp1;
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("sender = '%s'\n", sender);
-
-
- /*
- * Gather up sender_term
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- sender_term = cp1;
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("sender_term = '%s'\n", sender_term);
-
-
- /*
- * Gather up cookie
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- cookie = cp1;
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("cookie = '%s'\n", cookie);
-
- /*
- * Gather up signature
- */
- cp2 = cp1;
- while (*cp2 && cp2 < lim)
- cp2++;
- if(cp2 >= lim) return(1); /* over-length */
- signature = cp1;
- cp1 = cp2;
- cp1++;
- if(debug)
- printf("signature = '%s'\n", signature);
-
- return(0);
- }
-
-
- /*
- * delivers the message; returns NULL if OK, otherwise
- * a string describing the problem
- */
- char *deliver()
- {
- char *cp;
- int only_one;
- char *retval;
-
- while(cp = strchr(msg_text, '\015'))
- *cp = ' ';
-
- filter(msg_text);
- if(debug) printf("delivering message....\n");
-
- /* set only_one to false only if recip_term is "*" */
- only_one = 1;
- if (strcmp(recip_term, "*") == 0) {
- only_one = 0;
- }
-
- /* go through utmp entries, sending to appropriate ones */
- if (retval = first_utmp_entry())
- return(retval);
-
- do {
- if (debug) printf("evaluating utmp entry %s %s...\n",
- utmp.ut_name, utmp.ut_line);
- /* check for wrong recipient */
- if (*recipient) {
- if (strncmp(recipient, utmp.ut_name,
- sizeof(utmp.ut_name))) {
- continue;
- }
- }
- else {
- /* no recipient; if no term force console */
- if (*recip_term == '\0') {
- if (strncmp("console", utmp.ut_line,
- sizeof(utmp.ut_line))) {
- /* nope, wrong term */
- continue;
- }
- }
- }
-
- /* check for wrong term */
- if (*recip_term) {
- /* specific term or "*" */
- if (strcmp(recip_term, "*")) {
- /* specific term */
- if (strncmp(recip_term, utmp.ut_line,
- sizeof(utmp.ut_line))) {
- /* nope, wrong term */
- continue;
- }
- }
- }
-
- /* passed all tests, send it */
- retval = send_to_term(utmp.ut_line);
-
- /* see if once is enough */
- if (only_one && (retval == NULL))
- break;
-
- } while (next_utmp_entry(), utmp_file); /* keep going if more entries */
-
- return(NULL);
- }
-
- /*
- * first_utmp_entry
- *
- * opens utmp, calls next_utmp_entry and returns NULL if open is successful;
- * returns error string if open fails.
- */
- char *
- first_utmp_entry()
- {
- memset((char *) &utmp, 0, sizeof(utmp));
- utmp_file = fopen("/etc/utmp", "r");
- if(utmp_file == NULL) {
- perror("fopen /etc/utmp");
- return("unable to open /etc/utmp\n");
- }
- next_utmp_entry();
- return NULL;
- }
-
- /*
- * next_utmp_entry
- *
- * sets up next utmp entry with utmp_ok != 0,
- * closes file and utmp_ok == 0 if none.
- */
- void
- next_utmp_entry()
- {
- while (fread((char *) &utmp, sizeof(utmp), 1, utmp_file) == 1) {
- if (*utmp.ut_line && *utmp.ut_name) {
- return; /* utmp_file is non-NULL */
- }
- }
- /* if we get here, we're at eof; close & zero utmp_file */
- (void) fclose(utmp_file);
- utmp_file = NULL; /* indicates no more entries */
- }
-
- char *
- send_to_term(term)
- char *term;
- {
- /*
- * write to specific terminal if any, else console
- */
- char device[32];
- struct stat statbuf;
- int rc;
- FILE *stream;
- time_t aclock;
-
- sprintf(device, "/dev/%s", term);
- if (stat(device, &statbuf)) {
- perror("stat");
- return("unable to stat %s", device);
- }
-
- if (! (statbuf.st_mode & (S_IWGRP | S_IWOTH))) {
- if (debug) printf("won't write to %s because mode is %o\n",
- device, statbuf.st_mode);
- return("won't write because of mode");
- }
-
- stream = fopen(device, "w");
- if(stream == NULL) {
- perror("fopen");
- return("unable to write to %s", device);
- }
- time(&aclock);
- if(debug) printf("writing to %s\n", device);
- fprintf(stream,
- "\7\n---------\nConsole message from %s on %s%s---------\n",
- sender, asctime(localtime(&aclock)), msg_text);
- rc = fclose(stream);
- if (rc) {
- perror("fclose");
- return("unable to close %s", device);
- }
- if(debug) printf("delivery successful\n");
- return NULL;
- }
-
- /*
- * As noted in the RFC, it is important to filter out control
- * chracters and suchlike, since there may exist terminals
- * which will behave in bizarre and security-violating ways
- * if presented with certain control code sequences. The
- * client may also be filtering messages, but it is incumbent
- * upon a well-written server implementation not to rely on being
- * sent only "clean" messages.
- *
- * It is an open question as to how the filtering should be done.
- * One approach might be to squeeze out any invalid characters
- * silently. This would make debugging difficult. This implementation
- * replaces all non-printable characters with '?' characters.
- */
-
- void
- filter(text)
- char *text;
- {
- while (*text) {
- if(!isprint(*text) && !isspace(*text))
- *text = '?';
- text++;
- }
- }
-